package com.guofs.project.common.utils.tree;

import com.google.common.base.Predicate;
import com.guofs.project.common.utils.tree.base.impl.AbstractTreeBaseImpl;
import com.guofs.project.common.utils.tree.model.NodeModel;

import java.util.*;

/**
 * @author 郭芳诗
 * @date 2019/1/18 14:40
 */
public abstract class BaseNodeModelTree<N, K> extends AbstractTreeBaseImpl<N, K> {
    private static final long serialVersionUID = 5450571560561776168L;

    /**
     * 节点对照信息
     */
    private Map<K, NodeModel<N>> allNodes;
    /**
     * 树根<br/>
     * 包括找不到关系的节点
     */
    private List<NodeModel<N>> root;
    /**
     * 子节点对照其下一级节点数据信息
     */
    private Map<K, List<K>> childMap;

    private Map<K, NodeModel<N>> getAllNodes() {
        if (allNodes == null) {
            allNodes = new HashMap<K, NodeModel<N>>();
        }
        return allNodes;
    }

    private List<NodeModel<N>> getNodeModelRoot() {
        if (root == null) {
            root = new ArrayList<NodeModel<N>>();
        }
        return root;
    }

    protected BaseNodeModelTree(Collection<N> collection) {
        if (collection == null || collection.isEmpty()) {
            throw new NullPointerException("请传入有效集合");
        }
        for (N node : collection) {
            putNode(node);
        }
    }


    @Override
    protected Map<K, List<K>> getChildMap() {
        if (childMap == null) {
            childMap = new HashMap<K, List<K>>();
        }
        return childMap;
    }

    /**
     * 节点放置
     *
     * @param node 节点
     */
    @Override
    public void putNode(N node) {
        NodeModel<N> nodeModel = new NodeModel<N>(node, 0);
        getAllNodes().put(getKey(node), nodeModel);
        K parentKey = getParentKey(node);
        // 当父节点Key不存在的时候放的树根中
        if (isRoot(node)) {
            getNodeModelRoot().add(nodeModel);
        }else if (parentKey == null) {
            getNodeModelRoot().add(nodeModel);
        } else if (getChildMap().containsKey(parentKey)) {
            NodeModel<N> parentNode = getAllNodes().get(parentKey);
            nodeModel.setRank(parentNode.getRank() + 1);
            getChildMap().get(parentKey).add(getKey(node));
        } else {
            getNodeModelRoot().add(nodeModel);
        }
        getChildMap().put(getKey(node), new ArrayList<K>());
        // 整理树根节点
        List<NodeModel<N>> removeNodes = new ArrayList<NodeModel<N>>();
        for (NodeModel<N> root: getNodeModelRoot()) {
            if (isRoot(root.getNode())) {
                continue;
            }
            K pKey = getParentKey(root.getNode());
            if (getChildMap().containsKey(pKey)) {
                getChildMap().get(pKey).add(getKey(root.getNode()));
                removeNodes.add(root);
            }
        }
        for (NodeModel<N> removeNode: removeNodes) {
            getNodeModelRoot().remove(removeNode);
        }
    }

    /**
     * 遍历循环节点<br/>
     * <h2>函数内部</h2>  <br/>
     * true --> 继续循环 直至最终节点 ;<br/>
     * false --> 跳出循环
     *
     * @param predicate 操作节点对象函数
     */
    @Override
    public void forEach(Predicate<N> predicate) {
        for (Map.Entry<K, NodeModel<N>> entry: getAllNodes().entrySet()) {
            if (entry.getValue() == null) {
                throw new NullPointerException("节点数据不能为空。");
            }
            if (!predicate.apply(entry.getValue().getNode())) {
                break;
            }
        }
    }

    /**
     * 获取树根
     *
     * @return 树根节点数据
     */
    @Override
    public List<N> getRoot() {
        List<N> list = new ArrayList<N>();
        for (NodeModel<N> node: getNodeModelRoot()) {
            if (node == null) {
                throw new NullPointerException("节点数据不能为空。");
            }
            list.add(node.getNode());
        }
        return list;
    }

    /**
     * 依据唯一标识查找节点
     *
     * @param key 唯一标识
     * @return 该标识的节点
     */
    @Override
    public N getNodeByKey(K key) {
        NodeModel<N> node = getAllNodes().get(key);
        if (node != null) {
            return node.getNode();
        }
        return null;
    }

    /**
     * 获取父节点信息
     *
     * @param key 被获取父节点的子节点唯一标识
     * @return 父节点信息
     */
    @Override
    public N getParent(K key) {
        NodeModel<N> node = getAllNodes().get(key);
        if (node == null) {
            throw new NullPointerException("节点数据不能为空。");
        }
        K parentKey = getParentKey(node.getNode());
        if (parentKey != null) {
            NodeModel<N> parentNode = getAllNodes().get(parentKey);
            if (parentNode == null) {
                return null;
            }
            return parentNode.getNode();
        }
        return null;
    }

}
